﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Search;
using com.IronOne.BoardPACWinAppBO.Auth;
using com.IronOne.BoardPACWinAppBO.General;
using com.IronOne.BoardPACWinAppBO.Settings;
using com.IronOne.BoardPACWinAppDAO.DbOperations;
using com.IronOne.BoardPACWinAppDAO.DB;
using com.IronOne.BoardPACWinAppDAO.Mapper;
using com.IronOne.BoardPACWinAppUtil.Enums;
using com.IronOne.BoardPACWinAppUtil.Security;
using com.IronOne.BoardPACWinAppUtil.Util;
using SQLite;
using Version = com.IronOne.BoardPACWinAppDAO.DB.Version;

namespace com.IronOne.BoardPACWinAppDAO.Auth
{
    public class AuthDAO : IAuthDAO
    {
        private readonly IDbOperations _dbOperations;

        public AuthDAO()
        {
            _dbOperations = new DbOperations.DbOperations();
        }

        public async Task<UserModel> GetUserFromId(int userId)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                var user = await conn.Table<User>().Where(x => x.UserId == userId).FirstOrDefaultAsync();

                if (user == null) return null;
                return EntityToModelMapper.MapCategoryEntityToModel(user);
            }
            catch (Exception)
            {
                throw;
            }
        }

        public async Task<ResponseDataModel> InsertOrUpdateUserAsync(UserModel userM)
        {
            var rdm = new ResponseDataModel();
            try
            {
                var conn = await _dbOperations.GetAsyncConnectionOffline(DbConnectionType.UserDbConnetion, userM.UserId);

                var userEntity = await conn.Table<User>().Where(u => u.UserId == userM.UserId).FirstOrDefaultAsync();

                if (userEntity == null)
                {
                    userEntity = new User();
                    userEntity = ModelToEntityMapper.UserModelToEntity(userM, userEntity);
                    var queryResult = await conn.InsertAsync(userEntity);
                    rdm.Data = true;

                    if (queryResult != 1)
                        rdm.IsError = true;

                    return rdm;
                }

                var result = await conn.UpdateAsync(userEntity);
                rdm.Data = false;
                if (result != 1)
                    rdm.IsError = true;

                return rdm;
            }
            catch (Exception)
            {
                rdm.IsError = true;
            }
            return rdm;
        }

        public async Task<bool> InsertOrUpdateUserMapperAsync(UserModel userM)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);

                var userMapper = new UserMapper { UserId = userM.UserId, UserName = userM.UserName };

                var queryResult = await conn.UpdateAsync(userMapper);
                if (queryResult == 0)
                    queryResult = await conn.InsertAsync(userMapper);

                return queryResult == 1;
            }
            catch (Exception)
            {
            }
            return false;
        }

        public async Task<AppSettingModel> GetSupportInfoFromDb()
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);

                var asm = new AppSettingModel();

                var appSett1 =
                    await
                        conn.Table<AppSettings>()
                            .Where(x => x.SettingName.Equals("ShowSupportInfo"))
                            .FirstOrDefaultAsync();
                asm.ShowSupportInfo = Convert.ToBoolean(appSett1.SettingValue);

                var appSett2 =
                    await
                        conn.Table<AppSettings>().Where(x => x.SettingName.Equals("SupportName")).FirstOrDefaultAsync();
                asm.SupportInfoName = appSett2.SettingValue;

                var appSett3 =
                    await conn.Table<AppSettings>().Where(x => x.SettingName.Equals("Emails")).FirstOrDefaultAsync();
                asm.SupportInfoEmails = appSett3.SettingValue.Split(',');
                asm.SupportInfoDisplayEmails = appSett3.SettingValue.Replace(",", ", ");

                var appSett4 =
                    await
                        conn.Table<AppSettings>()
                            .Where(x => x.SettingName.Equals("SupportInfoPhone"))
                            .FirstOrDefaultAsync();
                asm.SupportInfoPhones = appSett4.SettingValue.Split(',');
                asm.SupportInfoDisplayPhones = appSett4.SettingValue.Replace(",", ", ");

                return asm;
            }
            catch (Exception)
            {
                var asm = new AppSettingModel();
                return asm;
            }
        }

        public async Task<ResponseDataModel> LogOnOffline(LogOnModel logOnModel)
        {
            try
            {
                var rdm = new ResponseDataModel();

                var userId = await GetUserId(logOnModel.UserName);
                if (userId == -1)
                {
                    rdm.IsError = true;
                    rdm.ErrorCode = "0";
                    rdm.ErrorMessage = "Username is incorrect";
                    return rdm;
                }

                rdm.Data = userId.ToString();

                SQLiteAsyncConnection connUsrAsync = null;
                try
                {
                    connUsrAsync =
                        await _dbOperations.GetAsyncConnectionOffline(DbConnectionType.UserDbConnetion, userId);
                }
                catch (Exception)
                {
                }

                if (connUsrAsync == null)
                {
                    rdm.IsError = true;
                    rdm.ErrorCode = "0";
                    rdm.ErrorMessage = "Username is incorrect";
                    return rdm;
                }

                var userEntity = await connUsrAsync.Table<User>().Where(x => x.UserId == userId).FirstOrDefaultAsync();
                if (userEntity == null)
                {
                    rdm.IsError = true;
                    rdm.ErrorCode = "0";
                    rdm.ErrorMessage = "User need to login to the system online atleast once";
                }
                else
                {
                    Global.IPadDisplayName = userEntity.iPadDisplayName;
                    var rslt = CryptoService.VerifyPasswordB(logOnModel.Password, userEntity.PasswordSalt, userEntity.Password);
                    if (!rslt)
                    {
                        rdm.IsError = true;
                        rdm.ErrorCode = "1";
                        rdm.ErrorMessage = "Password is incorrect";
                    }
                }
                return rdm;
            }
            catch (Exception)
            {
                throw;
            }
        }

        public async Task<object> GetSettingValueBySettingName(DatabaseType dbtype, string settingName)
        {
            try
            {
                if (dbtype == DatabaseType.GeneralDb)
                {
                    var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);
                    var appSett1 =
                        await
                            conn.Table<AppSettings>()
                                .Where(x => x.SettingName.Equals(settingName))
                                .FirstOrDefaultAsync();
                    return (object)appSett1.SettingValue;
                }

                if (dbtype == DatabaseType.UserDb)
                {
                    var conn = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                    var sett1 =
                        await conn.Table<Settings>().Where(x => x.SettingName.Equals(settingName)).FirstOrDefaultAsync();
                    object val = sett1.SettingValue;
                    return val;
                }
                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public async Task<object> GetSettingUsageValueBySettingName(string settingName)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                var sett1 =
                    await conn.Table<SettingsUsage>().Where(x => x.SettingName.Equals(settingName)).FirstOrDefaultAsync();
                if (sett1 == null) return null;

                return sett1.SettingValue;
            }
            catch (Exception)
            {
                return null;
            }
        }

        public async Task<bool> SetSettingUsageValueBySettingName(string settingName, string value)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                
                var appSett =
                    await conn.Table<SettingsUsage>().Where(x => x.SettingName.Equals(settingName)).FirstOrDefaultAsync();
                if (appSett == null) //Insert
                {
                    var aSet = new SettingsUsage
                    {
                        SettingName = settingName,
                        SettingValue = value
                    };
                    await conn.InsertAsync(aSet);
                }
                else //Update
                {
                    appSett.SettingValue = value;
                    await conn.UpdateAsync(appSett);
                }
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        public async Task<ResponseDataModel> WipeoutDevice()
        {
            var rdm = new ResponseDataModel();
            try
            {
                var deviceModel = new DeviceModel { DeviceRegistrationStatus = (int)DeviceStatus.Wipeout };
                await SetDeviceAuthStatus(deviceModel);

                try
                {
                    var localFolder4 = await ApplicationData.Current.LocalFolder.GetFolderAsync("Profile");
                    await localFolder4.DeleteAsync(StorageDeleteOption.PermanentDelete);
                }
                catch (Exception)
                {
                }

                #region Logical data flush

                try
                {
                    using (var conn = _dbOperations.GetSyncConnection(DbConnectionType.GeneralDbConnection))
                    {
                        conn.DeleteAll<UserMapper>();
                    }
                }
                catch (Exception)
                {
                }

                #endregion

                try
                {
                    var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync();
                    foreach (var folder in folders)
                    {
                        try
                        {
                            var path = Path.Combine(ApplicationData.Current.LocalFolder.Path, folder.Name,
                                folder.Name + ".sqlite");

                            #region Logical data flush

                            using (var conn = new SQLiteConnection(path))
                            {
                                try
                                {
                                    conn.DeleteAll<AnnotationShareByUser>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<AnnotationShareWithUser>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<DB.Comments>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Heading>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Log>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<MainCategory>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<DB.Meeting>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Member>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Paper>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Settings>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<SettingsUsage>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<SubCategory>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<User>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<Version>();
                                }
                                catch (Exception)
                                {
                                }
                                try
                                {
                                    conn.DeleteAll<AnnotationRestore>();
                                }
                                catch (Exception)
                                {
                                }
                            }

                            #endregion

                            #region Physical folder and file deletions

                            try
                            {
                                var localFolder1 = await ApplicationData.Current.LocalFolder.GetFolderAsync(folder.Name);
                                await localFolder1.DeleteAsync(StorageDeleteOption.PermanentDelete);
                            }
                            catch (Exception)
                            {
                            }

                            try
                            {
                                var localFolder2 = await ApplicationData.Current.LocalFolder.GetFolderAsync(folder.Name);
                                var localFolder3 = await localFolder2.GetFolderAsync(ApplicationConstants.PDF);
                                await localFolder3.DeleteAsync(StorageDeleteOption.PermanentDelete);
                            }
                            catch (Exception)
                            {
                            }

                            #endregion
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
                catch (Exception)
                {
                }
            }
            catch (Exception e)
            {
                rdm.IsError = true;
                rdm.ErrorMessage = e.Message;
            }
            return rdm;
        }

        public async Task<ResponseDataModel> ResetDevice()
        {
            var rdm = new ResponseDataModel();
            try
            {
                #region Logical data flush

                /*using (SQLiteConnection conn = _dbOperations.GetSyncConnection(DbConnectionType.UserDbConnetion))
                {
                    try { conn.DeleteAll<AnnotationShareByUser>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<AnnotationShareWithUser>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<DB.Comments>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<Heading>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<Log>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<MainCategory>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<DB.Meeting>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<Member>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<Paper>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<Settings>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<SubCategory>(); }
                    catch (Exception) { }
                    try { conn.DeleteAll<DB.Version>(); }
                    catch (Exception) { }
                }*/

                var connA = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                var user = await connA.Table<User>().Where(x => x.UserId == Global.UserId).FirstOrDefaultAsync();
                user.AccessesTimeStamp = null;
                user.CategoryTimeStamp = null;
                user.CommentsTimeStamp = null;
                user.HeadingTimeStamp = null;
                user.LastProfileImageSyncDateTicks = null;
                user.MeetingsTimeStamp = null;
                user.MembersTimeStamp = null;
                user.NewsItemTimeStamp = null;
                user.PaperTimeStamp = null;
                user.SecurityTimeStamp = null;
                user.SettingTimeStamp = null;
                user.ShareAnnotTimeStamp = null;
                user.SharedDocTimeStamp = null;
                user.SubCategoryTimeStamp = null;
                user.TimeStamp = null;

                var result = await connA.UpdateAsync(user);
                rdm.Data = result;

                #endregion
            }
            catch (Exception e)
            {
                rdm.IsError = true;
                rdm.ErrorMessage = e.Message;
            }
            return rdm;
        }

        public async Task<bool> CleanDecryptedAgendaItems()
        {
            try
            {
                var documentsFolder =
                    await
                        StorageFolder.GetFolderFromPathAsync(Path.Combine(ApplicationData.Current.TemporaryFolder.Path));

                var fileTypeFilter = new List<string> { ApplicationConstants.PDFEXT };
                var queryOptions = new QueryOptions(CommonFileQuery.OrderByName, fileTypeFilter);

                var results = documentsFolder.CreateFileQueryWithOptions(queryOptions);
                var files = await results.GetFilesAsync();

                foreach (var item in files)
                {
                    try
                    {
                        await item.DeleteAsync(StorageDeleteOption.PermanentDelete);
                    }
                    catch (Exception)
                    {
                    }
                }

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        ///     Get user id from username
        ///     returns -1 if user does not exists
        /// </summary>
        /// <param name="userName"></param>
        /// <returns>user id</returns>
        private async Task<int> GetUserId(string userName)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);
                var user =
                    await
                        conn.Table<UserMapper>()
                            .Where(u => u.UserName.ToLower().Equals(userName.ToLower()))
                            .FirstOrDefaultAsync();
                return user.UserId;
            }
            catch (Exception)
            {
                return -1;
            }
        }

        public async Task<Dictionary<string, object>> GetSettingValuesBySettingNames(DatabaseType dbtype,
            string[] settingNames)
        {
            try
            {
                var settingList = new Dictionary<string, object>();

                switch (dbtype)
                {
                    case DatabaseType.GeneralDb:
                        foreach (var item in settingNames)
                        {
                            var conn1 = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);
                            var item1 = item;
                            var appSett1 =
                                await conn1.Table<AppSettings>().Where(x => x.SettingName.Equals(item1)).FirstOrDefaultAsync();
                            settingList.Add(item, appSett1.SettingValue);
                        }
                        return settingList;
                    case DatabaseType.UserDb:
                        var conn2 = _dbOperations.GetAsyncConnection(DbConnectionType.UserDbConnetion);
                        foreach (var item in settingNames)
                        {
                            var item1 = item;
                            var sett1 =
                                await conn2.Table<Settings>().Where(x => x.SettingName.Equals(item1)).FirstOrDefaultAsync();
                            settingList.Add(item, sett1.SettingValue);
                        }
                        return settingList;
                }

                return null;
            }
            catch (Exception)
            {
                throw;
            }
        }

        

        #region Device

        private const string Sname = "DeviceRegistrationStatus";

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public async Task<DeviceModel> GetDeviceAuthStatus()
        {
            var dm = new DeviceModel();
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);

                var appSett =
                    await conn.Table<AppSettings>().Where(x => x.SettingName.Equals(Sname)).FirstOrDefaultAsync();
                if (appSett == null)
                {
                    var aSet = new AppSettings
                    {
                        SettingName = Sname,
                        SettingValue = ((int)DeviceStatus.NotRegisterd).ToString()
                    };
                    await conn.InsertAsync(aSet);

                    dm.DeviceRegistrationStatus = (int)DeviceStatus.NotRegisterd;
                    return dm;
                }

                dm.DeviceRegistrationStatus = Convert.ToInt32(appSett.SettingValue);
                return dm;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        ///     Insert or update DeviceRegistrationStatus on the db
        /// </summary>
        /// <param name="deviceModel"></param>
        /// <returns></returns>
        public async Task<bool> SetDeviceAuthStatus(DeviceModel deviceModel)
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);

                var appSett =
                    await conn.Table<AppSettings>().Where(x => x.SettingName.Equals(Sname)).FirstOrDefaultAsync();
                if (appSett == null)
                {
                    var aSet = new AppSettings
                    {
                        SettingName = Sname,
                        SettingValue = deviceModel.DeviceRegistrationStatus.ToString()
                    };
                    await conn.InsertAsync(aSet);
                }
                else
                {
                    appSett.SettingValue = deviceModel.DeviceRegistrationStatus.ToString();
                    var success = await conn.UpdateAsync(appSett);
                }
            }
            catch (Exception)
            {
                throw;
            }
            return true;
        }

        #endregion


        public async Task<string> CheckAndInitiateDateFormat()
        {
            try
            {
                var conn = _dbOperations.GetAsyncConnection(DbConnectionType.GeneralDbConnection);

                var appSett =
                    await
                        conn.Table<AppSettings>()
                            .Where(
                                x => x.SettingName.Equals(ApplicationConstants.GeneralDbSettingNames.DateFormatSelected))
                            .FirstOrDefaultAsync();
                var val = appSett.SettingValue.Split('#')[0];
                if (val.Equals("-1"))
                {
                    var sett = await GetSettingValueBySettingName(DatabaseType.UserDb, ApplicationConstants.UserDbSettingNames.dateOnlyFormat);
                    val = sett.ToString();
                }
                return val;
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}